/**
 * Copyright 2020 Huawei Technologies Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "aicpu_optimizer.h"

#include <dirent.h>
#include <regex.h>
#include <sys/types.h>
#include <map>
#include <mutex>
#include <set>
#include <string>
#include <utility>

#include "config/config_file.h"
#include "error_code/error_code.h"
#include "ge/ge_api_types.h"
#include "graph/debug/ge_attr_define.h"
#include "graph/ge_context.h"
#include "graph/op_kernel_bin.h"
#include "graph/utils/attr_utils.h"
#include "util/constant.h"
#include "util/log.h"
#include "util/util.h"

namespace {
constexpr const char *kPlaceholderOpType = "PlaceHolder";
constexpr const char *kFrameworkOp = "FrameworkOp";
constexpr const char *kEndOpType = "End";
constexpr const char *kCustKernelSoPathBasedOnEnv =
    "op_impl/custom/cpu/aicpu_kernel/custom_impl/";
constexpr const char *kLibCpuKernelsSoPathBasedOnEnv =
    "opp/op_impl/built-in/aicpu/aicpu_kernel/lib/";
constexpr const char *kLibCpuKernelsSoName = "libcpu_kernels.so";
constexpr const char *kLibAicpuKernelsSoName = "libaicpu_kernels.so";
constexpr const char *kLibPtKernelsSoName = "libpt_kernels.so";
constexpr const char *kCustomKernelSoRelativePath =
    "/ops/op_impl/custom/cpu/aicpu_kernel/custom_impl/";
constexpr const char *kCpuKernelsSoRelativePath =
    "/ops/op_impl/built-in/aicpu/aicpu_kernel/lib/";
constexpr const char *kAscendAicpuPathEnvName = "ASCEND_AICPU_PATH";
// load libcpu_kernels.so in model
constexpr uint64_t kLoadTypeForCpuKernelsInModel = 1;
// load libcpu_kernels.so in installation package
constexpr uint64_t kLoadTypeForCpuKernelsInPkg = 0;
// regex pattern for libcpu_kernels.so
constexpr const char *kPatternForCpuKernelsSo = "libcpu_kernels_v.+\\.so";
// regex pattern for libpt_kernels.soF
constexpr const char *kPatternForPtKernelsSo = "libpt_kernels_v.+\\.so";

// global variable: so name with version map, key: so name, value: so name with
// version
std::map<std::string, std::string> g_so_with_version_map;
// global variable: mutex to prevent g_so_with_version_map
std::mutex g_so_with_version_map_mutex;
// global variable: <graph_id, soName>
std::set<std::pair<uint32_t, std::string>> g_so_in_graph_set;
// global variable: mutex for g_soInGraph set
std::mutex g_so_in_graph_set_mutex;
// soc version
std::string g_soc_version = "Ascend310";
}  // namespace

namespace aicpu {

using AttrValueMap = google::protobuf::Map<string, aicpuops::AttrValue>;

OptimizerPtr AicpuOptimizer::instance_ = nullptr;

OptimizerPtr AicpuOptimizer::Instance() {
  static std::once_flag flag;
  std::call_once(flag,
                 [&]() { instance_.reset(new (std::nothrow) AicpuOptimizer); });
  return instance_;
}

ge::Status AicpuOptimizer::Initialize() {
  InitLoadCpuKernelsType();
  Optimizer::InitOpCheckMode();

  std::string soc_version;
  if (ge::GetContext().GetOption(ge::SOC_VERSION, soc_version) ==
      ge::GRAPH_SUCCESS) {
    AICPUE_LOGI("Get soc version [%s] success.", soc_version.c_str());
  } else {
    AICPUE_LOGEVENT("Get soc version failed. Please check!");
    return ge::SUCCESS;
  }
  CheckAndSetSocVersion(soc_version);
  return ge::SUCCESS;
}

void AicpuOptimizer::CheckAndSetSocVersion(
    const std::string &soc_version_from_ge) {
  if (soc_version_from_ge.find("Ascend310") == 0) {
    g_soc_version = "Ascend310";
  } else if (soc_version_from_ge.find("Ascend910") == 0) {
    g_soc_version = "Ascend910";
  } else if (soc_version_from_ge.find("Ascend710") == 0) {
    g_soc_version = "Ascend710";
  } else {
    AICPUE_LOGEVENT("Check soc version [%s] failed, Please check!",
                    soc_version_from_ge.c_str());
  }
}

ge::Status AicpuOptimizer::OptimizeFusedGraph(
    ge::ComputeGraph &graph,
    const std::map<string, OpFullInfo> &all_op_info) const {
  uint32_t graph_id = 0;
  bool exist_graph_id = true;
  if (!ge::AttrUtils::GetInt(graph, kAttrNameRootGraphId, graph_id)) {
    exist_graph_id = false;
    AICPUE_LOGW("Failed to get attr _root_graph_id from subgraph.");
  }
  AICPUE_LOGI("Success to get attr[%s] from subgraph[%u].",
              kAttrNameRootGraphId.c_str(), graph_id);

  for (ge::NodePtr &node : graph.GetDirectNode()) {
    AICPU_CHECK_NOTNULL(node)
    ge::OpDescPtr op_desc_ptr = node->GetOpDesc();
    AICPU_CHECK_NOTNULL(op_desc_ptr)
    std::string op_type = op_desc_ptr->GetType();
    AICPU_IF_BOOL_EXEC(
        ((op_type == kPlaceholderOpType) || (op_type == kEndOpType)),
        AICPUE_LOGD("Current op type is [%s]. Don't need to fuse.",
                    op_type.c_str());
        continue)
    // if op type is framework_op, get original op
    AICPU_IF_BOOL_EXEC(
        (op_type == kFrameworkOp),
        AICPU_CHECK_RES(GetFrameworkOpType(op_desc_ptr, op_type)))

    // now only fuse aicpu op
    std::string kernel_lib_name =
        GetKernelLibNameByOpType(op_type, all_op_info);
    if ((kernel_lib_name != kAicpuKernelInfoChoice) &&
        (kernel_lib_name != kCustAicpuKernelInfoChoice)) {
      continue;
    }
    auto iter = all_op_info.find(op_type);
    AICPU_IF_BOOL_EXEC(iter == all_op_info.end(),
                       AICPU_REPORT_INNER_ERROR(
                           "Can't find op type[%s] in op info store, op[%s].",
                           op_type.c_str(), node->GetName().c_str());
                       return ErrorCode::CREATE_NODEDEF_FAILED)
    OpFullInfo op_full_info = iter->second;
    std::string kernel_so = op_full_info.kernelSo;
    std::string func_name = op_full_info.functionName;
    bool param_len = op_full_info.flagAsync;
    int workspace_size = op_full_info.workspaceSize;
    FWKAdapter::FWKExtTopicType topic_type = op_full_info.topicType;

    bool user_defined = op_full_info.userDefined;
    (void)ge::AttrUtils::SetStr(op_desc_ptr, "kernelSo", kernel_so);
    (void)ge::AttrUtils::SetStr(op_desc_ptr, "funcName", func_name);
    (void)ge::AttrUtils::SetBool(op_desc_ptr, "paramLen", param_len);
    (void)ge::AttrUtils::SetInt(op_desc_ptr, "workspaceSize", workspace_size);
    (void)ge::AttrUtils::SetStr(op_desc_ptr, "opKernelLib", kernel_lib_name);
    (void)ge::AttrUtils::SetInt(op_desc_ptr, kTopicType, topic_type);
    (void)ge::AttrUtils::SetBool(op_desc_ptr, kCustAicpuFlag, false);

    if ((op_check_mode_) && (!user_defined) &&
        (kernel_so == kLibAicpuKernelsSoName)) {
      (void)ge::AttrUtils::SetStr(op_desc_ptr, "needCheckCpu", op_type);
    }
    AICPU_CHECK_RES_WITH_LOG(
        CheckAndSetUnknowType(op_desc_ptr, all_op_info),
        "Call CheckAndSetUnknowType function failed. op[%s].",
        node->GetName().c_str())

    AICPU_CHECK_RES(SetCustKernelBinFile(op_desc_ptr, all_op_info, graph_id,
                                         exist_graph_id))
    aicpuops::NodeDef node_def;
    BuildAicpuNodeDef(op_desc_ptr, node_def);

    std::string node_def_str;
    AICPU_CHECK_FALSE_EXEC(
        node_def.SerializeToString(&node_def_str),
        AICPU_REPORT_INNER_ERROR("The serialization from nodedef probuf to str failed, op[%s].",
                                 node->GetName().c_str());
        return ErrorCode::CREATE_NODEDEF_FAILED)

    const uint8_t *node_def_data =
        reinterpret_cast<const uint8_t *>(node_def_str.data());
    AICPU_CHECK_FALSE_EXEC(
        ge::AttrUtils::SetZeroCopyBytes(
            op_desc_ptr, kCustomizedOpDef,
            ge::Buffer::CopyFrom(node_def_data, node_def_str.length())),
        AICPU_REPORT_CALL_ERROR(
            "Failed to call SetZeroCopyBytes to set tf node def, op[%s].",
            node->GetName().c_str());
        return ErrorCode::CREATE_NODEDEF_FAILED)
  }

  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetCustKernelBinFile(
    ge::OpDescPtr op_desc_ptr,
    const std::map<std::string, OpFullInfo> &all_op_info, uint32_t graph_id,
    bool exist_graph_id) const {
  std::string op_type = op_desc_ptr->GetType();
  OpFullInfo op_full_info;
  auto iter = all_op_info.find(op_type);
  AICPU_IF_BOOL_EXEC(
      iter == all_op_info.end(),
      AICPU_REPORT_INNER_ERROR("can not find op type[%s] for op[%s].",
                               op_type.c_str(), op_desc_ptr->GetName().c_str());
      return ErrorCode::NONE_KERNELINFOSTORE)
  op_full_info = iter->second;

  std::string cust_kernel_so_real_path;
  if (!CheckSoNeedLoadInModel(op_full_info, cust_kernel_so_real_path)) {
    return ge::SUCCESS;
  }
  AICPU_IF_BOOL_EXEC(
      cust_kernel_so_real_path.empty(),
      AICPU_REPORT_INNER_ERROR("Get cust kernel so real path failed.");
      return ErrorCode::STR_IS_EMPTY);
  AICPU_IF_BOOL_EXEC(
      (!PackageBinFile(op_desc_ptr, cust_kernel_so_real_path, op_full_info,
                       graph_id, exist_graph_id)),
      AICPU_REPORT_CALL_ERROR(
          "Call PackageBinFile function failed, op[%s].",
          op_desc_ptr->GetName().c_str());
      return ErrorCode::PACKAGE_BIN_FILE);
  return ge::SUCCESS;
}

const string AicpuOptimizer::GetCustKernelSoPath() const {
  std::string cust_kernel_so_real_path;
  const char *path_env = std::getenv("ASCEND_OPP_PATH");
  if (path_env != nullptr) {
    std::string path = path_env;
    if (path[path.size() - 1] != '/') {
      path.append("/");
    }
    cust_kernel_so_real_path = path + kCustKernelSoPathBasedOnEnv;
  } else {
    std::string real_file_path =
        GetSoPath(reinterpret_cast<void *>(&AicpuOptimizer::Instance));
    AICPU_IF_BOOL_EXEC(real_file_path.empty(),
                       AICPU_REPORT_CALL_ERROR(
                           "Call GetSoPath function failed, so path is empty.");
                       return cust_kernel_so_real_path);
    cust_kernel_so_real_path =
        Stringcat(real_file_path, "../../../..", kCustomKernelSoRelativePath);
  }
  return cust_kernel_so_real_path;
}

const std::string AicpuOptimizer::GetCpuKernelSoPath() const {
  std::string cpu_kernel_so_real_path;
  const char *path_env = std::getenv(kAscendAicpuPathEnvName);
  if (path_env != nullptr) {
    std::string path = path_env;
    if (path[path.size() - 1] != '/') {
      path.append("/");
    }
    cpu_kernel_so_real_path =
        path + kLibCpuKernelsSoPathBasedOnEnv + g_soc_version + "/";
  } else {
    std::string real_file_path =
        GetSoPath(reinterpret_cast<void *>(&AicpuOptimizer::Instance));
    AICPU_IF_BOOL_EXEC(real_file_path.empty(),
                       AICPU_REPORT_CALL_ERROR(
                           "Call GetSoPath function failed, so path is empty.");
                       return cpu_kernel_so_real_path);
    cpu_kernel_so_real_path =
        Stringcat(real_file_path, "../../../..", kCpuKernelsSoRelativePath,
                  g_soc_version, "/");
  }
  return cpu_kernel_so_real_path;
}

bool AicpuOptimizer::PackageBinFile(ge::OpDescPtr op_desc_ptr,
                                    const std::string &bin_folder_path,
                                    const OpFullInfo &op_full_info,
                                    uint32_t graph_id,
                                    bool exist_graph_id) const {
  std::string bin_file_name;
  if (GetBinFileName(op_full_info, bin_folder_path, bin_file_name) !=
      ge::SUCCESS) {
    AICPU_REPORT_CALL_ERROR("Call GetBinFileName failed.");
    return false;
  }

  if (exist_graph_id) {
    std::lock_guard<std::mutex> so_in_graph_set_mutex(g_so_in_graph_set_mutex);
    std::pair<uint32_t, std::string> pair =
        std::make_pair(graph_id, bin_file_name);
    auto iter = g_so_in_graph_set.find(pair);
    if (iter != g_so_in_graph_set.end()) {
      AICPUE_LOGI("So [%s] has exist in current sub graph [%u].",
                  bin_file_name.c_str(), graph_id);
      // kernel_so need refresh to real name for libcpu_kernels_${version}.so
      (void)ge::AttrUtils::SetStr(op_desc_ptr, "kernelSo", bin_file_name);
      (void)ge::AttrUtils::SetBool(op_desc_ptr, kCustAicpuFlag, true);
      return true;
    }
    (void)g_so_in_graph_set.insert(pair);
    AICPUE_LOGI("Save soName [%s] and graph_id [%u] success.",
                bin_file_name.c_str(), graph_id);
  }

  std::string bin_file_path = bin_folder_path;
  AICPU_IF_BOOL_EXEC(
      (bin_file_name.empty() || bin_file_path.empty()),
      AICPU_REPORT_INNER_ERROR("bin file name[%s] or bin file path[%s] is "
                               "empty. please check json file.",
                               bin_file_name.c_str(), bin_file_path.c_str());
      return false);

  if (bin_file_path[bin_file_path.size() - 1] != '/') {
    bin_file_path.append("/");
  }
  bin_file_path.append(bin_file_name);

  AICPUE_LOGI("Bin file path is [%s]", bin_file_path.c_str());
  std::vector<char> buffer;
  bool ret = ReadBytesFromBinaryFile(bin_file_path, buffer);
  if (!ret) {
    AICPU_REPORT_INNER_ERROR(
        "Call ReadBytesFromBinaryFile failed"
        " to read bin file[%s].",
        bin_file_path.c_str());
    return false;
  }

  std::string key = op_desc_ptr->GetName();

  ge::OpKernelBinPtr cust_aicpu_kernel_ptr =
      std::make_shared<ge::OpKernelBin>(key, move(buffer));
  AICPU_IF_BOOL_EXEC(
      cust_aicpu_kernel_ptr == nullptr,
      AICPU_REPORT_INNER_ERROR("Create OpKernelBin object failed.");
      return false);
  op_desc_ptr->SetExtAttr(ge::OP_EXTATTR_CUSTAICPU_KERNEL,
                          cust_aicpu_kernel_ptr);
  // kernel_so need refresh to real name for libcpu_kernels_${version}.so
  (void)ge::AttrUtils::SetStr(op_desc_ptr, "kernelSo", bin_file_name);
  (void)ge::AttrUtils::SetBool(op_desc_ptr, kCustAicpuFlag, true);

  AICPUE_LOGI("Set extend attr OP_EXTATTR_CUSTAICPU_KERNEL success!");
  return true;
}

void AicpuOptimizer::InitLoadCpuKernelsType() {
  // get load_type_for_cpu_kernels from config file.
  std::string load_type_for_cpu_kernels;
  if (ConfigFile::GetInstance().GetValue(kLoadCpuKernelsInModel,
                                         load_type_for_cpu_kernels)) {
    uint64_t result = kDefaultLoadTypeForCpuKernels;
    if (StringToNum(load_type_for_cpu_kernels, result).state != ge::SUCCESS) {
      AICPUE_LOGW(
          "Tran LoadCpuKernelsInModel [%s] to integer failed. default value is "
          "0.",
          load_type_for_cpu_kernels.c_str());
      return;
    }
    // if load_type_for_cpu_kernels from config file is not 0 or 1, print
    // warning log.
    if ((result != kLoadTypeForCpuKernelsInModel) &&
        (result != kLoadTypeForCpuKernelsInPkg)) {
      AICPUE_LOGW("load_type_for_cpu_kernels is [%s], default value is [%lu].",
                  load_type_for_cpu_kernels.c_str(),
                  kDefaultLoadTypeForCpuKernels);
      return;
    }
    load_type_for_cpu_kernels_ = result;
    return;
  }
  AICPUE_LOGW(
      "Get OpFusionMinNum from config file failed. default value is 2.");
}

bool AicpuOptimizer::CheckSoNeedLoadInModel(OpFullInfo &op_full_info,
                                            std::string &file_path) const {
  // only user defined kernels and libcpu_kernels.so and libpt_kernels.so
  // perhaps load in model
  if ((!op_full_info.userDefined) &&
      (op_full_info.kernelSo != kLibCpuKernelsSoName) &&
      (op_full_info.kernelSo != kLibPtKernelsSoName)) {
    return false;
  }
  // user defined kernels need load in model
  if (op_full_info.userDefined) {
    file_path = GetCustKernelSoPath();
    return true;
  }

  // libcpu_kernels.so and libpt_kernels.so load in model only when
  // load_type_for_cpu_kernels_ == 1
  if (load_type_for_cpu_kernels_ != kLoadTypeForCpuKernelsInModel) {
    return false;
  }
  // libpt_kernels.so must load in model
  file_path = GetCpuKernelSoPath();
  return true;
}

ge::Status AicpuOptimizer::GetBinFileName(const OpFullInfo &op_full_info,
                                          const std::string &bin_folder_path,
                                          std::string &bin_file_name) const {
  // get so name for user defined kernels from json config file
  std::string kernel_so = op_full_info.kernelSo;
  if (op_full_info.userDefined) {
    bin_file_name = kernel_so;
    return ge::SUCCESS;
  }

  std::lock_guard<std::mutex> so_with_version_map_lock(
      g_so_with_version_map_mutex);
  auto iter = g_so_with_version_map.find(kernel_so);
  if (iter != g_so_with_version_map.end()) {
    bin_file_name = iter->second;
    AICPUE_LOGI("Kernel so name [%s] has been searched, real bin file name is [%s]",
                kernel_so.c_str(), bin_file_name.c_str());
    return ge::SUCCESS;
  }

  // realpath
  std::string folder_path = RealPath(bin_folder_path);
  AICPU_IF_BOOL_EXEC(
      ((folder_path.empty()) || (folder_path != bin_folder_path)),
      AICPU_REPORT_INNER_ERROR("Get realpath[%s] failed, [%s]",
                               bin_folder_path.data(), strerror(errno));
      return GET_BIN_FILE_NAME_FAILED)

  // open dir
  DIR *dir;
  dir = opendir(folder_path.c_str());
  AICPU_IF_BOOL_EXEC(
      dir == nullptr,
      AICPU_REPORT_INNER_ERROR("Open dir[%s] failed.", folder_path.c_str());
      return GET_BIN_FILE_NAME_FAILED)
  uint32_t so_count = 0;
  dirent *direntp = nullptr;
  // max search 1024 files to prevent dead circulation
  for (int index = 0; index < 1024; index++) {
    direntp = readdir(dir);
    if (direntp == nullptr) {
      break;
    }
    // DT_REG: a regular file
    if (direntp->d_type != DT_REG) {
      continue;
    }
    // check libcpu_kernels.so
    std::string file_name(direntp->d_name);
    AICPUE_LOGI(
        "file_name is [%s], file_name length is [%u], d_name is [%s], d_reclen is "
        "[%u].",
        file_name.c_str(), file_name.length(), direntp->d_name,
        direntp->d_reclen);
    std::string pattern = (kernel_so == kLibPtKernelsSoName)
                              ? kPatternForPtKernelsSo
                              : kPatternForCpuKernelsSo;
    if (!ValidateStr(file_name, pattern)) {
      continue;
    }

    bin_file_name = file_name;
    so_count++;
    // check so count: only one is allowed.
    AICPU_IF_BOOL_EXEC(
        so_count > 1, (void)closedir(dir); AICPU_REPORT_INNER_ERROR(
            "multi cpu kernels so exist in dir path[%s].", folder_path.c_str());
        return MULTI_CPU_KERNELS_SO_EXIST)
  }

  // check libcpu_kernels.so exist
  AICPU_IF_BOOL_EXEC(
      so_count == 0, (void)closedir(dir); AICPU_REPORT_INNER_ERROR(
          "no cpu kernels so exist in dir path[%s].", folder_path.c_str());
      return NO_CPU_KERNELS_SO_EXIST)

  // record bin_file_name
  g_so_with_version_map[kernel_so] = bin_file_name;
  AICPUE_LOGI("Kernel so is [%s], bin file name is [%s].", kernel_so.c_str(),
              bin_file_name.c_str());
  ;

  // close dir: 0 success
  if (closedir(dir) != 0) {
    AICPUE_LOGW("close dir failed, errno: [%s], dir path: [%s].", strerror(errno),
                folder_path.c_str());
  }
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::BuildAicpuNodeDef(
    const ge::OpDescPtr &op_desc_ptr, aicpuops::NodeDef &node_def) const {
  std::string op_type = op_desc_ptr->GetType();
  node_def.set_op(op_type);

  bool is_unknow_shape = false;
  if (ge::AttrUtils::HasAttr(op_desc_ptr, ge::ATTR_NAME_UNKNOWN_SHAPE_TYPE)) {
    // ATTR_NAME_UNKNOWN_SHAPE_TYPE attr exist, means unknow shape
    is_unknow_shape = true;
  }

  size_t input_num = op_desc_ptr->GetInputsSize();
  for (size_t i = 0; i < input_num; ++i) {
    aicpuops::Tensor *input_tensor = node_def.add_inputs();
    AICPU_CHECK_NOTNULL(input_tensor)
    ge::GeTensorDesc input_desc = op_desc_ptr->GetInputDesc(i);
    auto aicpu_shape = input_tensor->mutable_tensor_shape();
    AICPU_CHECK_NOTNULL(aicpu_shape)
    for (const auto &dim : input_desc.GetShape().GetDims()) {
      aicpuops::TensorShape_Dim *aicpu_dims = aicpu_shape->add_dim();
      AICPU_CHECK_NOTNULL(aicpu_dims)
      aicpu_dims->set_size(dim);
    }
    aicpu_shape->set_data_format(
        static_cast<ge::Format>(input_desc.GetFormat()));
    aicpu_shape->set_unknown_rank(is_unknow_shape);
    input_tensor->set_tensor_type(
        static_cast<ge::DataType>(input_desc.GetDataType()));
  }

  size_t output_num = op_desc_ptr->GetOutputsSize();
  for (size_t i = 0; i < output_num; ++i) {
    aicpuops::Tensor *output_tensor = node_def.add_outputs();
    AICPU_CHECK_NOTNULL(output_tensor)
    ge::GeTensorDesc output_desc = op_desc_ptr->GetOutputDesc(i);
    auto aicpu_shape = output_tensor->mutable_tensor_shape();
    AICPU_CHECK_NOTNULL(aicpu_shape)
    for (const auto &dim : output_desc.GetShape().GetDims()) {
      aicpuops::TensorShape_Dim *aicpu_dims = aicpu_shape->add_dim();
      AICPU_CHECK_NOTNULL(aicpu_dims)
      aicpu_dims->set_size(dim);
    }
    aicpu_shape->set_data_format(
        static_cast<ge::Format>(output_desc.GetFormat()));
    aicpu_shape->set_unknown_rank(is_unknow_shape);
    output_tensor->set_tensor_type(
        static_cast<ge::DataType>(output_desc.GetDataType()));
  }

  auto attrs_map = op_desc_ptr->GetAllAttrs();
  for (auto iter = attrs_map.begin(); iter != attrs_map.end(); ++iter) {
    const std::string &attr_name = iter->first;
    aicpuops::AttrValue attr_value;
    const ge::GeAttrValue::ValueType ge_value_type =
        (iter->second).GetValueType();
    AICPUE_LOGD("Get attr:[%s] value from op [%s], ge_value_type is [%d].",
                attr_name.c_str(), op_type.c_str(), ge_value_type);

    AICPUE_LOGI("Get Attr name: [%s].", attr_name.c_str());
    // If get attr value failed, no need to insert the attr. Only print log
    if (GetAttrValueFromGe(op_desc_ptr, attr_name, ge_value_type, attr_value) !=
        ge::SUCCESS) {
      AICPUE_LOGW("GetAttrValueFromGe attr_name[%s] for op[%s] failed.",
                  attr_name.c_str(), op_type.c_str());
      continue;
    }

    AICPU_CHECK_NOTNULL(node_def.mutable_attrs())
    auto pair = node_def.mutable_attrs()->insert(
        AttrValueMap::value_type(attr_name, attr_value));
    AICPU_CHECK_FALSE_EXEC(
        pair.second,
        AICPUE_LOGW("Node [%s] insert attr [%s] to nodeDef failed.",
                    op_desc_ptr->GetName().c_str(), attr_name.c_str()));
  }

  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::GetAttrValueFromGe(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    const ge::GeAttrValue::ValueType ge_value_type,
    aicpuops::AttrValue &attr_value) const {
  std::string op_type = op_desc_ptr->GetType();
  switch (ge_value_type) {
    case ge::GeAttrValue::ValueType::VT_STRING: {
      AICPU_CHECK_RES(SetValueTypeString(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_FLOAT: {
      AICPU_CHECK_RES(SetValueTypeFloat(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_BOOL: {
      AICPU_CHECK_RES(SetValueTypeBool(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_INT: {
      AICPU_CHECK_RES(SetValueTypeInt(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_TENSOR: {
      AICPU_CHECK_RES(
          SetValueTypeAicpuTensor(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_DATA_TYPE: {
      AICPU_CHECK_RES(
          SetValueTypeAicpuDataType(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_LIST_FLOAT: {
      AICPU_CHECK_RES(SetValueTypeListFloat(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_LIST_LIST_INT: {
      AICPU_CHECK_RES(
          SetValueTypeListAicpuShape(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_LIST_DATA_TYPE: {
      AICPU_CHECK_RES(
          SetValueTypeListAicpuDataType(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_LIST_INT: {
      AICPU_CHECK_RES(SetValueTypeListInt(op_desc_ptr, attr_name, attr_value))
      break;
    }
    case ge::GeAttrValue::ValueType::VT_LIST_STRING: {
      AICPU_CHECK_RES(
          SetValueTypeListString(op_desc_ptr, attr_name, attr_value))
      break;
    }
    default: {
      AICPUE_LOGW("op [%s] Currently can not support attr value type of [%d].",
                  op_type.c_str(), ge_value_type);
      return ge::SUCCESS;
    }
  }
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeString(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  string string_value;
  CHECK_RES_BOOL(ge::AttrUtils::GetStr(op_desc_ptr, attr_name, string_value),
                 ErrorCode::DATA_TYPE_UNDEFILED,
                 AICPU_REPORT_CALL_ERROR(
                     "Call ge::AttrUtils::GetStr function failed to get"
                     " attr[%s], op[%s].",
                     attr_name.c_str(), op_desc_ptr->GetName().c_str()))

  attr_value.set_s(string_value);
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeFloat(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  float float_value = 0.0;
  CHECK_RES_BOOL(ge::AttrUtils::GetFloat(op_desc_ptr, attr_name, float_value),
                 ErrorCode::DATA_TYPE_UNDEFILED,
                 AICPU_REPORT_CALL_ERROR(
                     "Call ge::AttrUtils::GetFloat function failed to get"
                     " attr[%s], op[%s].",
                     attr_name.c_str(), op_desc_ptr->GetName().c_str()))
  attr_value.set_f(float_value);
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeBool(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  bool bool_value = false;
  CHECK_RES_BOOL(ge::AttrUtils::GetBool(op_desc_ptr, attr_name, bool_value),
                 ErrorCode::DATA_TYPE_UNDEFILED,
                 AICPU_REPORT_CALL_ERROR(
                     "Call ge::AttrUtils::GetBool function failed to get"
                     " attr[%s], op[%s].",
                     attr_name.c_str(), op_desc_ptr->GetName().c_str()))
  attr_value.set_b(bool_value);
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeInt(
    const ge::OpDescPtr &op_desc_ptr, const string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  int64_t int_value = 0;
  CHECK_RES_BOOL(ge::AttrUtils::GetInt(op_desc_ptr, attr_name, int_value),
                 ErrorCode::DATA_TYPE_UNDEFILED,
                 AICPU_REPORT_CALL_ERROR(
                     "Call ge::AttrUtils::GetInt function failed to get"
                     " attr[%s], op[%s].",
                     attr_name.c_str(), op_desc_ptr->GetName().c_str()))
  attr_value.set_i(int_value);
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeListFloat(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  std::vector<float> float_list;
  CHECK_RES_BOOL(
      ge::AttrUtils::GetListFloat(op_desc_ptr, attr_name, float_list),
      ErrorCode::DATA_TYPE_UNDEFILED,
      AICPU_REPORT_CALL_ERROR("Call ge::AttrUtils::GetListFloat function failed"
                              " to get attr[%s], op[%s].",
                              attr_name.c_str(),
                              op_desc_ptr->GetName().c_str()))
  for (const float &f : float_list) {
    auto array = attr_value.mutable_array();
    AICPU_CHECK_NOTNULL(array)
    array->add_f(f);
  }
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeAicpuTensor(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  ge::ConstGeTensorPtr ge_tensor;
  CHECK_RES_BOOL(ge::AttrUtils::GetTensor(op_desc_ptr, attr_name, ge_tensor),
                 ErrorCode::DATA_TYPE_UNDEFILED,
                 AICPU_REPORT_CALL_ERROR(
                     "Call ge::AttrUtils::GetTensor function failed"
                     " to get attr[%s], op[%s].",
                     attr_name.c_str(), op_desc_ptr->GetName().c_str()))
  AICPU_CHECK_NOTNULL(ge_tensor)

  ge::DataType ge_data_type = ge_tensor->GetTensorDesc().GetDataType();
  aicpuops::Tensor *aicpu_tensor = attr_value.mutable_tensor();
  AICPU_CHECK_NOTNULL(aicpu_tensor)
  aicpu_tensor->set_tensor_type(static_cast<ge::DataType>(ge_data_type));

  aicpuops::TensorShape *aicpu_shape = aicpu_tensor->mutable_tensor_shape();
  AICPU_CHECK_NOTNULL(aicpu_shape)
  for (const auto &dim : ge_tensor->GetTensorDesc().GetShape().GetDims()) {
    aicpuops::TensorShape_Dim *aicpu_dims = aicpu_shape->add_dim();
    AICPU_CHECK_NOTNULL(aicpu_dims)
    aicpu_dims->set_size(dim);
  }

  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeAicpuDataType(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  ge::DataType ge_type;
  CHECK_RES_BOOL(ge::AttrUtils::GetDataType(op_desc_ptr, attr_name, ge_type),
                 ErrorCode::DATA_TYPE_UNDEFILED,
                 AICPU_REPORT_CALL_ERROR(
                     "Call ge::AttrUtils::GetDataType function failed"
                     " to get attr[%s], op[%s].",
                     attr_name.c_str(), op_desc_ptr->GetName().c_str()))
  attr_value.set_type(static_cast<ge::DataType>(ge_type));
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeListAicpuShape(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  std::vector<std::vector<int64_t>> shape_value;
  CHECK_RES_BOOL(
      ge::AttrUtils::GetListListInt(op_desc_ptr, attr_name, shape_value),
      ErrorCode::DATA_TYPE_UNDEFILED,
      AICPU_REPORT_CALL_ERROR("Call ge::AttrUtils::GetListListInt function"
                              " failed to get attr[%s], op[%s].",
                              attr_name.c_str(),
                              op_desc_ptr->GetName().c_str()))

  auto attr_list = attr_value.mutable_array();
  AICPU_CHECK_NOTNULL(attr_list)
  for (const vector<int64_t> &shape : shape_value) {
    aicpuops::TensorShape *shape_tensor = attr_list->add_shape();
    AICPU_CHECK_NOTNULL(shape_tensor)
    for (const int64_t &dim : shape) {
      AICPU_IF_BOOL_EXEC(
          (dim < 0), AICPUE_LOGW("The dim [%ld] in shape is less than 0.", dim);
          return ErrorCode::DATA_TYPE_UNDEFILED)
      aicpuops::TensorShape_Dim *aicpu_dims = shape_tensor->add_dim();
      AICPU_CHECK_NOTNULL(aicpu_dims)
      aicpu_dims->set_size(dim);
    }
  }
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeListAicpuDataType(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  AICPUE_LOGD("Get attr_name[%s] data type list.", attr_name.c_str());
  std::vector<ge::DataType> ge_type_list;
  CHECK_RES_BOOL(
      ge::AttrUtils::GetListDataType(op_desc_ptr, attr_name, ge_type_list),
      ErrorCode::DATA_TYPE_UNDEFILED,
      AICPU_REPORT_CALL_ERROR("Call ge::AttrUtils::GetListDataType function"
                              " failed to get attr[%s], op[%s].",
                              attr_name.c_str(),
                              op_desc_ptr->GetName().c_str()))

  for (ge::DataType &ge_type : ge_type_list) {
    AICPUE_LOGD("AttrName[%s] getype[%d].", attr_name.c_str(), ge_type);
    AICPU_CHECK_NOTNULL(attr_value.mutable_array())
    attr_value.mutable_array()->add_type(static_cast<ge::DataType>(ge_type));
  }
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeListInt(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  // if not find in json file, the attr is truely list_int
  std::vector<int32_t> list_int_value;
  CHECK_RES_BOOL(
      ge::AttrUtils::GetListInt(op_desc_ptr, attr_name, list_int_value),
      ErrorCode::DATA_TYPE_UNDEFILED,
      AICPU_REPORT_CALL_ERROR("Call ge::AttrUtils::GetListInt function"
                              " failed to get attr[%s], op[%s].",
                              attr_name.c_str(),
                              op_desc_ptr->GetName().c_str()))
  for (int32_t value : list_int_value) {
    AICPU_CHECK_NOTNULL(attr_value.mutable_array())
    attr_value.mutable_array()->add_i(value);
  }
  return ge::SUCCESS;
}

ge::Status AicpuOptimizer::SetValueTypeListString(
    const ge::OpDescPtr &op_desc_ptr, const std::string &attr_name,
    aicpuops::AttrValue &attr_value) const {
  std::vector<std::string> list_string_value;
  CHECK_RES_BOOL(
      ge::AttrUtils::GetListStr(op_desc_ptr, attr_name, list_string_value),
      ErrorCode::DATA_TYPE_UNDEFILED,
      AICPU_REPORT_CALL_ERROR("Call ge::AttrUtils::GetListStr function"
                              " failed to get attr[%s], op[%s].",
                              attr_name.c_str(),
                              op_desc_ptr->GetName().c_str()))

  for (std::string value : list_string_value) {
    AICPU_CHECK_NOTNULL(attr_value.mutable_array())
    attr_value.mutable_array()->add_s(value);
  }
  return ge::SUCCESS;
}

FACTORY_GRAPH_OPTIMIZER_CLASS_KEY(AicpuOptimizer, "AICPUOptimizer")
}  // namespace aicpu
